package com.amt.olcacon.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.json.JSONUtil;
import com.amt.olcacon.db.DerbyConfig;
import com.amt.olcacon.entity.ContributionTree;
import com.amt.olcacon.mapper.ContributionTreeMapper;
import com.amt.olcacon.service.IContributionTreeService;
import com.amt.olcacon.utils.TreeItemByImpactCategory;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.openlca.core.DataDir;
import org.openlca.core.database.ImpactMethodDao;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.math.SystemCalculator;
import org.openlca.core.model.CalculationSetup;
import org.openlca.core.model.Process;
import org.openlca.core.model.descriptors.ImpactDescriptor;
import org.openlca.core.results.LcaResult;
import org.openlca.core.results.UpstreamNode;
import org.openlca.core.results.UpstreamTree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.eclipse.persistence.jpa.jpql.Assert.fail;

@Slf4j
@Service
public class ContributionTreeServiceImpl extends ServiceImpl<ContributionTreeMapper, ContributionTree> implements IContributionTreeService {


    @Autowired
    private Snowflake sfe;


    @Override
    public void ContributionTree(Long processId, Long methodId, Long calculationResult) {

        Map<String, Long> map = new HashMap<>();
        var r = calculate(processId, methodId);

        List<ContributionTree> contributionTrees = new ArrayList<>();
        if (r != null && r.impactIndex() != null) {
            r.impactIndex().forEach(impactDescriptor -> {
                UpstreamTree tree = UpstreamTree.of(r.provider(), impactDescriptor);
                // 根节点总结果
                BigDecimal rootResult = BigDecimal.valueOf(tree.root.result());
                var treeRoot = convert(tree.root, calculationResult, impactDescriptor.id, 0L, impactDescriptor.name, rootResult, BigDecimal.valueOf(tree.root.requiredAmount()), contributionTrees);
                contributionTrees.add(treeRoot);

                List<UpstreamNode> childs = tree.childs(tree.root);

                addChilds(tree, childs, methodId, impactDescriptor, map, contributionTrees, calculationResult, impactDescriptor.id, treeRoot.getId(), treeRoot.getResult(), treeRoot.getRequired_amount());
            });
        }
        log.info("Total contributionTrees before save: {}", contributionTrees.size());

        try {
            boolean ok = this.saveBatch(contributionTrees, 200); // 每200条一批，避免大批量插入失败
            log.info("saveBatch result: {}", ok);

            // 再打印一次数据库中的条数进行对比
            Long dbCount = this.lambdaQuery()
                    .eq(ContributionTree::getCalculation_result, calculationResult)
                    .count();
            log.info("DB count for calculationResult {} = {}", calculationResult, dbCount);

            if (dbCount < contributionTrees.size()) {
                log.warn("⚠️ 数据丢失！预期 {} 条，实际插入 {} 条",
                        contributionTrees.size(), dbCount);
            }
        } catch (Exception e) {
            log.error("保存 ContributionTree 失败: {}", e.getMessage(), e);
        }
    }


    public void addChilds(UpstreamTree tree, List<UpstreamNode> childs, Long methodId,
                          ImpactDescriptor impactDescriptor,
                          Map<String, Long> map, List<ContributionTree> contributionTrees,
                          Long calculationResult, Long impactCategory,
                          Long parentId, BigDecimal parentResult, BigDecimal requiredAmount) {

        for (UpstreamNode child : childs) {

            if (child.provider().flow().id <= 10000000000L) {
                // 乘以requiredAmount
                BigDecimal finalDirect = BigDecimal.valueOf(child.directContribution()).multiply(requiredAmount);
                contributionTrees.stream().filter(t -> t.getId().equals(parentId)).findFirst().ifPresent(t -> t.setDirect(finalDirect));
                continue;
            }

            Long processId = child.provider() != null && child.provider().provider() != null
                    ? child.provider().provider().id
                    : null;

            // 生成唯一 key：processId + impactCategory
            String key = processId + "_" + impactCategory;

            // 转换成 ContributionTree 并加入集合
            //var treeNode = convert(child, calculationResult, impactCategory, parentId, impactDescriptor.name, rootResult);
            var treeNode = convert(child, calculationResult, impactCategory, parentId, impactDescriptor.name, parentResult, requiredAmount, contributionTrees);
            contributionTrees.add(treeNode);

            log.info("Add node: flowName={}, provider={}, parentId={}, result={}, direct={}",
                    treeNode.getFlow_name(), treeNode.getProvider(), parentId,
                    child.result(), child.directContribution());

            // 判断是否继续递归
            if (!map.containsKey(key)) {
                map.put(key, processId);

//                var r = calculate(processId, methodId);
//                if (r != null) {
//                    var tree = UpstreamTree.of(r.provider(), impactDescriptor);
//                    List<UpstreamNode> childNodes = tree.childs(tree.root);
//                    addChilds(childNodes, methodId, impactDescriptor,
//                            map, contributionTrees, calculationResult,
//                            impactCategory, treeNode.getId(), treeNode.getResult(),treeNode.getRequired_amount());
//                }

                List<UpstreamNode> childNodes = tree.childs(tree.root);
                if (childNodes != null && !childNodes.isEmpty()) {
                    addChilds(tree, childNodes, methodId, impactDescriptor,
                            map, contributionTrees, calculationResult,
                            impactCategory, treeNode.getId(), treeNode.getResult(), treeNode.getRequired_amount());
                }

            }
        }
    }

    public LcaResult calculate(Long processId, Long methodId) {

        try (var db = DataDir.get().openDatabase(DerbyConfig.DBNAME)) {
            // 使用ProcessDao确保正确加载流程
            ProcessDao processDao = new ProcessDao(db);

            Process process = processDao.getForId(processId);

            if (process == null) {
                log.info("流程不存在！");
                return null;
            }

            var method = new ImpactMethodDao(db)
                    .getForId(methodId);

            var setup = CalculationSetup.of(process)
                    .withImpactMethod(method);

            var calc = new SystemCalculator(db);
            LcaResult lcaResult = calc.calculate(setup);
            db.close();
            return lcaResult;
        } catch (Exception ex) {
            log.info("计算失败！");
            return null;

        }
    }

    /**
     * 转换成ContributionTree结构
     */
    public ContributionTree convert(UpstreamNode node, Long calculationResult, Long impactCategory,
                                    Long parentId, String impactCatgoryName, BigDecimal parentResult,
                                    BigDecimal ParentRequiredAmount, List<ContributionTree> contributionTrees) {

        ContributionTree tree = new ContributionTree();
        if (node != null) {
//            BigDecimal scaling = BigDecimal.ZERO;
//            if (rootResult != null && rootResult.compareTo(BigDecimal.ZERO) > 0) {
//                scaling = BigDecimal.valueOf(node.result())
//                        .divide(rootResult, 10, RoundingMode.HALF_UP);
//            }

            BigDecimal scaling = BigDecimal.ZERO;
            if (parentResult != null && parentResult.compareTo(BigDecimal.ZERO) != 0) {
                scaling = BigDecimal.valueOf(Math.abs(node.result())).divide(BigDecimal.valueOf(Math.abs(parentResult.doubleValue())), 10, RoundingMode.HALF_UP);
            }
            BigDecimal direct = BigDecimal.valueOf(node.directContribution());
            if (node.provider() != null && node.provider().flow() != null && node.provider().flow().id < 10000000000L) {
                if (0 != node.directContribution() && 0 != node.requiredAmount()) {
                    // 乘以requiredAmount
                    direct = BigDecimal.valueOf(node.directContribution()).multiply(ParentRequiredAmount);
//                    BigDecimal finalDirect = direct;
//                    contributionTrees.stream().filter(t->t.getId().equals(parentId)).findFirst().ifPresent(t->t.setDirect(finalDirect));
                }
            }

            tree.setId(sfe.nextId())
                    .setCalculation_result(calculationResult)
                    .setImpact_category(impactCategory)
                    .setFlow_name(node.provider() != null && node.provider().flow() != null ? node.provider().flow().name : null)
                    .setProvider(node.provider() != null && node.provider().provider() != null ? node.provider().provider().name : null)
                    .setFlow_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().id : null)
                    .setFlow_ref_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().refId : null)
                    .setFlow_type(node.provider() != null && node.provider().flow() != null ? String.valueOf(node.provider().flow().flowType) : null)
                    .setFlow_property_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().refFlowPropertyId : null)
                    .setResult(!Double.isNaN(node.result()) ? BigDecimal.valueOf(node.result()) : null)
                    .setDirect(direct)
                    .setRequired_amount(!Double.isNaN(node.requiredAmount()) ? BigDecimal.valueOf(node.requiredAmount()) : null)
//                    .setScaling(!Double.isNaN(node.scalingFactor()) ? BigDecimal.valueOf(node.scalingFactor()) : null)
                    .setScaling(scaling)
                    .setParent_id(parentId)
                    .setImpactCategoryName(impactCatgoryName)
            ;

            tree.setHas_childs(node.result() > 0 && node.directContribution() == 0);

            String nodeType;
            if (node.provider() == null) {
                nodeType = "UNKNOWN";
            } else if (node.provider().flow() != null && node.provider().provider() != null) {
                nodeType = "PROCESS"; // 这是一个完整的单元过程，既有流信息也有提供该流的流程信息
            } else if (node.provider().flow() != null) {
                nodeType = "FLOW"; // 代表直接排放或初始输入，只有流信息，没有提供它的工艺过程
            } else {
                nodeType = "UNKNOWN"; // 理论上不应出现，用于容错
            }
            tree.setNodeType(nodeType); // 需要在ContributionTree实体中添加这个字段

        }

        return tree;


    }


    @Override
    public void contributionTreeNew(Long processId, Long methodId, Long calculationResult) {
        LcaResult result = null;
        List<ContributionTree> contributionTrees = new ArrayList<>();
        try {
            result = calculate(processId, methodId);

            // 对每个影响类别获取并显示贡献树
            for (ImpactDescriptor impact : result.impactIndex()) {
                double totalValue = result.getTotalImpactValueOf(impact);
                if (Math.abs(totalValue) > 1e-16) {

                    // 创建贡献树
                    UpstreamTree tree = UpstreamTree.of(result.provider(), impact);

                    getNode(contributionTrees, tree, tree.root, 1, totalValue, calculationResult, 0L, impact);
                }
            }

            log.info("保存贡献树数据...:{}", JSONUtil.toJsonStr(contributionTrees));

            var contributionTreesByImpactCategory =contributionTrees.stream().collect(Collectors.groupingBy(ContributionTree::getImpact_category));

            for (Map.Entry<Long, List<ContributionTree>> entry : contributionTreesByImpactCategory.entrySet()) {
                ImpactDescriptor impact =result.impactIndex().getForId(entry.getKey());
                TreeItemByImpactCategory.testShowTreeItemComment(entry.getValue(),result, impact,contributionTrees);
            }

            log.info("加入了 enviFlow 的贡献树数据...:{}", JSONUtil.toJsonStr(contributionTrees));

            boolean ok = this.saveBatch(contributionTrees, 200); // 每200条一批，避免大批量插入失败

        } catch (Exception e) {
            fail("计算过程中发生错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 释放结果资源
            if (result != null) {
                result.dispose();
            }
        }


    }

    /**
     * 递归获取贡献树节点及其子节点
     *
     * @param tree       贡献树对象
     * @param node       当前节点
     * @param level      层级深度
     * @param totalValue 总贡献值，用于计算百分比
     */
    private void getNode(List<ContributionTree> contributionTrees, UpstreamTree tree, UpstreamNode node, int level, double totalValue, Long calculationResult, Long parentId, ImpactDescriptor impactDescriptor) {
        // 缩进，显示层级关系
        String indent = "  ".repeat(level);

        // 计算贡献比例
        double sharePercentage = (node.result() / totalValue) * 100;

        // 打印节点信息，包含层级标识
        System.out.printf("%s[%d] 贡献比例: %.2f%% ,输入输出：%s-%s, 工序: %s-%s, 总贡献: %.4f, 直接贡献: %.4f\n",
                indent,
                level,
                sharePercentage,
                node.provider().flow().id,
                node.provider().flow().name,
                node.provider().provider().id,
                node.provider().provider().name,
                node.result(),
                node.directContribution());

        var addData = convertNew(node, calculationResult, impactDescriptor.id, parentId, impactDescriptor.name, sharePercentage);
        contributionTrees.add(addData);

        // 递归打印子节点
        List<UpstreamNode> childs = tree.childs(node);
        if (childs != null && !childs.isEmpty()) {
            for (UpstreamNode child : childs) {
                getNode(contributionTrees, tree, child, level + 1, totalValue, calculationResult, addData.getId(), impactDescriptor);
            }
        }
    }

    /**
     * 转换成ContributionTree结构
     */
    public ContributionTree convertNew(UpstreamNode node, Long calculationResult, Long impactCategory, Long parentId, String impactCatgoryName, double sharePercentage) {

        ContributionTree tree = new ContributionTree();
        if (node != null) {

            tree.setId(sfe.nextId())
                    .setCalculation_result(calculationResult)
                    .setImpact_category(impactCategory)
                    .setFlow_name(node.provider() != null && node.provider().flow() != null ? node.provider().flow().name : null)
                    .setProvider(node.provider() != null && node.provider().provider() != null ? node.provider().provider().name : null)
                    .setFlow_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().id : null)
                    .setFlow_ref_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().refId : null)
                    .setFlow_type(node.provider() != null && node.provider().flow() != null ? String.valueOf(node.provider().flow().flowType) : null)
                    .setFlow_property_id(node.provider() != null && node.provider().flow() != null ? node.provider().flow().refFlowPropertyId : null)
                    .setResult(!Double.isNaN(node.result()) ? BigDecimal.valueOf(node.result()) : null)
                    .setDirect(BigDecimal.valueOf(node.directContribution()))
                    .setRequired_amount(!Double.isNaN(node.requiredAmount()) ? BigDecimal.valueOf(node.requiredAmount()) : null)
                    .setScaling(BigDecimal.valueOf(sharePercentage))
                    .setParent_id(parentId)
                    .setImpactCategoryName(impactCatgoryName)
            ;

            tree.setHas_childs(node.result() > 0 && node.directContribution() == 0);

        }

        return tree;


    }


}
